#!/usr/bin/perl -w
###############################################################################
# $Id: vcl.pm 1075500 2012-04-08 19:22:55Z stallam $
###############################################################################
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###############################################################################
=head1 NAME

VCL::Provisioning::vcl - VCL module to support povisioning of vcl machines on other cloud

=head1 SYNOPSIS

 Needs to be written

=head1 DESCRIPTION

 This module provides provisioning source to provision machines on another VCL cloud

=cut

##############################################################################

package VCL::Module::Provisioning::vcl;

# Specify the lib path using FindBin
use FindBin;
use lib "$FindBin::Bin/../../..";

# Configure inheritance
use base qw(VCL::Module::Provisioning);

# Specify the version of this module
our $VERSION = '2.2.1';

# Specify the version of Perl to use
use 5.008000;

use strict;
use warnings;
use diagnostics;
use English qw( -no_match_vars );
use Scalar::Util;
use VCL::utils;

$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;

##############################################################################
=head1 OBJECT METHODS

=cut

#/////////////////////////////////////////////////////////////////////////////

=head2 capture

 Parameters  : $request_data_hash_reference
 Returns     : 1 if sucessful, 0 if failed
 Description : Saves the image back into the repository.

=cut
sub rnotify {
	my $log_message = shift;
	notify($ERRORS{'DEBUG'}, 0, $log_message);
	#open(OUTPUT, ">>", "/usr/local/vcl/lib/VCL/Module/Provisioning/log");
	#print OUTPUT "$log_message\n";
	#close OUTPUT;
}
sub capture {
	rnotify("VCL, should I support capturing an image?");
	return 1;
} ## end sub capture


#/////////////////////////////////////////////////////////////////////////////

sub brokerRequest {
        my $imgid = $_[0];
        my $start = $_[1];
        my $len = $_[2];
	my $reservationid = $_[3];
	my $request_id = $_[4];
	my $db_img_id = $_[5];
	rnotify("image id : $imgid");
        rnotify("start : $start");
        rnotify("len: $len");
        rnotify("reservation id : $reservationid");
        my @argument_string = ("XMLRPCaddRequest", $imgid, $start, $len);
        my $xml_ret = xmlrpc_call(@argument_string);
        my $status = '';
	$status = $xml_ret->value->{status} if(defined($xml_ret->value->{status}));
        rnotify("status of addRequest: $status");
	if ($status eq '') {
		return 0;
	}
        my $requestid = -1;
        $requestid = $xml_ret->value->{requestid} if(defined($xml_ret->value->{requestid}));
        rnotify("requestid in addRequest: $requestid");
        $requestid = $requestid + 0;
        if ($requestid == -1) {
		my $msg = $xml_ret->value->{errormsg};
		rnotify("requestid in addRequest: $msg");
                return -1;
        } else {
                rnotify("Loop until machine is ready");
                my $trys = 240;
		while($trys > 0) {
                        $trys -= 1;
                        my $method = "XMLRPCgetRequestStatus";
                        my @arguments = ($method, $xml_ret->value->{requestid});
			rnotify(format_data(@arguments));
                        my $xml_res = xmlrpc_call(@arguments);
                        rnotify(format_data($xml_res));
                        $status = $xml_res->value->{status};
                        rnotify("status of getRequestStatus: $status");
                        if ($status eq "error"){
                                return -1;
                        } elsif ($status eq "failed") {
                                return -1;
                        }elsif ($status eq "timedout") {
                                return -1;
                        }elsif ($status eq "future") {
                                return -1;
                        }elsif ($status eq "ready") {
				#now fetch the connection data and push into database
				my $ipaddress = getLocalAddress();
				my @argument_array = ("XMLRPCgetRequestConnectData", $xml_ret->value->{requestid}, $ipaddress);
				rnotify(format_data(@argument_array));
        			my $xml_resp = xmlrpc_call(@argument_array);
			        my $status = $xml_resp->value->{status};
			        rnotify("Status of connection data : $status");
			        if ($status eq "error"){
			                return -1;
			        } elsif ($status eq "notready"){
			                return -1;
			        } elsif ($status eq "ready") {
			                my $serverip = $xml_resp->value->{serverIP};
        	        		my $user = $xml_resp->value->{user};
			                my $passwd = $xml_resp->value->{password};
                			my @result = ($serverip, $user, $passwd);
		                	rnotify(format_data(@result));
	                		#update computer table, reservation table and new vclinstances table
			                return updateDatabaseEntries($reservationid, $requestid, $serverip, $user, $passwd, $request_id, $db_img_id);
				}
                        }elsif ($status eq "loading") {
                                my $t = $xml_res->value->{time};
                                rnotify("status of getRequestStatus: $t mins");
                        }
                        sleep(5);
                }
        }
        return 1;
}
#/////////////////////////////////////////////////////////////////////////////
=head2 provision

 Parameters  : hash
 Returns     : 1(success) or 0(failure)
 Description : loads node with provided image.  This includes, setting up any files (copying or registering).  Loading the resource.  Turn on the resource.  Wait until the resource is ready to use (after the vm or physical machine has booted).
In terms of VCL-to-VCL, we fire an XMLRPC request for "XMLRPCaddRequest" for the imageid, and wait until the VCL cloud returns some status for the request. Currently, waiting on loop and issue "XMLRPCgetRequestStatus" continously until it returns with some status

Note: allow computer to load only for state = new, for rest just return 1.

=cut

sub load {
	my $self = shift;
	if (ref($self) !~ /vcl/i) {
		rnotify("subroutine was called as a function, it must be called as a class method");
		return 0;
	}
	my ($package, $filename, $line, $sub) = caller(0);
	rnotify("vcl.pm, the load method has been successfully called");
	my $request_state_name = $self->data->get_request_state_name();
	rnotify("request state name : $request_state_name");
	if ($request_state_name ne 'new') {
		return 1;
	}	
	# Store some hash variables into local variables
        # to pass to write_current_image routine

        #my $request_data = $self->data->get_request_data;

        #if (!$request_data) {
        #        notify($ERRORS{'WARNING'}, 0, "unable to retrieve request data hash");
        #        return 0;
        #}

	# Store some hash variables into local variables
	my $request_id = $self->data->get_request_id();
	rnotify("request id : $request_id");
	#get start and end time from request table
	#my $select_statement = "select start AS request_start, end AS request_end from request where id=$request_id";
	#my @selected_rows = database_select($select_statement);
	#if (scalar @selected_rows == 0) {
	#	rnotify("Empty rows selected");
	#	return -1;
	#}
	#my $start_time = 0;
	#my $end_time = 0;
	#for (@selected_rows) {
	#	my %reservation_row = %{$_};
	#	$start_time = $reservation_row{request_start};
	#	$end_time = $reservation_row{request_end};
	#}
	my $reservation_id = $self->data->get_reservation_id();
	my $image_id = $self->data->get_image_id();
	rnotify("image id: $image_id");
	#get imageid on another cloud for given imagename
        my $select_statement = "select cloudimageid AS cloud_imageid from vclImage where imageid=$image_id";
        my @selected_rows = database_select($select_statement);
        if (scalar @selected_rows == 0) {
               rnotify("Empty rows selected");
               return -1;
        }
	my $cloud_image_id = 4;
        for (@selected_rows) {
               my %image_row = %{$_};
               $cloud_image_id = $image_row{cloud_imageid};
        }

	my $start_time = $self->data->get_request_start_time(0);
	my $end_time = $self->data->get_request_end_time(0);
	my $user = $self->data->get_user_login_id();
	rnotify("user logged in: $user");
	rnotify("start time : $start_time");
	rnotify("end time: $end_time");
	#get password of corresponding user
	my $passwd = getPassword($user);
	rnotify("password: $passwd");
	my $startepoch = convert_to_epoch_seconds($start_time);
	my $currepoch = time();
	if ($startepoch <= $currepoch) {
		$startepoch = $currepoch;
	}
        my $endepoch = convert_to_epoch_seconds($end_time);
        my $len  = ($endepoch - $startepoch)/60; #api needs in minutes
	rnotify("start epoch time : $startepoch");
        rnotify("end epoch time: $endepoch");
	#retrieve IPaddress of cloud from ipaddress table
	my $vcladdr = getCloudAddress();
	rnotify("cloud address $vcladdr");
	$XMLRPC_URL = "https://$vcladdr/vcl/index.php?mode=xmlrpccall";
	$XMLRPC_USER = $user;
	$XMLRPC_PASS = $passwd;

	#make XMLRPC API call to addRequest, this API returns only after machine is up or on some error or on 240*5 timeout seconds
	my $status = brokerRequest($cloud_image_id, $startepoch, $len, $reservation_id, $request_id, $image_id);
	if ($status == 1){
		return 1;
	} else { 
		return 0;
	}
		
} ## end sub load
#/////////////////////////////////////////////////////////////////////////////

sub getCloudAddress {
	#select statement
	my $stmt = "SELECT cloudip FROM ipaddress";
	my @selected_rows = database_select($stmt);
	# Check to make sure 1 row was returned
        if (scalar @selected_rows == 0) {
                return 1;
        }
        elsif (scalar @selected_rows > 1) {
                return 0;
        }
	my $cloudip = $selected_rows[0]{cloudip};
	return $cloudip;
}

#/////////////////////////////////////////////////////////////////////////////

sub getPassword {
	my $user = shift;
        #select statement
        my $stmt = "SELECT password FROM users where user='$user'";
        my @selected_rows = database_select($stmt);
        # Check to make sure 1 row was returned
        if (scalar @selected_rows == 0) {
                return 1;
        }
        elsif (scalar @selected_rows > 1) {
                return 0;
        }
        my $password = $selected_rows[0]{password};
        return $password;
}

#/////////////////////////////////////////////////////////////////////////////

sub getLocalAddress {
        #select statement
        my $stmt = "SELECT localip FROM ipaddress";
        my @selected_rows = database_select($stmt);
        # Check to make sure 1 row was returned
        if (scalar @selected_rows == 0) {
                 rnotify("row is selected");
        }
        elsif (scalar @selected_rows > 1) {
		return 0;
        }
        my $localip = $selected_rows[0]{localip};
        return $localip;
}

#/////////////////////////////////////////////////////////////////////////////
sub updateDatabaseEntries {
	my $reservationid = $_[0];
	my $requestid = $_[1];
	my $serverip = $_[2];
	my $user = $_[3];
	my $passwd = $_[4];
	my $request_id = $_[5];
	my $db_img_id = $_[6];
	rnotify("reservation id : $reservationid");
	rnotify("request id : $requestid");	
	rnotify("server i : $serverip");
	rnotify("user id : $user");
	rnotify("password : $passwd");
	rnotify("request id in this cloud : $request_id");
	#update reservation table
	my $reservation_update = " UPDATE reservation SET remoteIP = \'$serverip\', pw =\'$passwd\'  WHERE id = $reservationid ";
        # Call the database execute subroutine
        if (database_execute($reservation_update)) {
               rnotify("reservationid is updated");
        }
        else {
                rnotify("unable to update reservationid");
		return -1;
        }
	#select computerid for given reservation id
	my $select_statement = "SELECT computerid FROM reservation WHERE id = $reservationid";
	my @selected_rows = database_select($select_statement);
        # Check to make sure 1 row was returned
        if (scalar @selected_rows == 0) {
		rnotify("failed to get computerid from reservation");
		return -1;
        }
        elsif (scalar @selected_rows > 1) {
		return -1;
        }
        my $computerid = $selected_rows[0]{computerid};
	#update computer table
	my $computer_update = "UPDATE computer set IPaddress = \'$serverip\' WHERE id = $computerid";
	# Call the database execute subroutine
        if (database_execute($computer_update)) {
               rnotify("computerid is updated");
        }
        else {
                rnotify("unable to update computerid");
		return -1;
        }
	#update vclinstance table
	my $vcl_update = "INSERT INTO vclinstances(rpcid, computerid, imageid, reservationid, requestid, username) VALUES($requestid, $computerid, $db_img_id, $reservationid, $request_id, \'$user\')";
	# Call the database execute subroutine
        if (database_execute($vcl_update)) {
               rnotify("vclinstance is updated");
        }
        else {
                rnotify("unble to update vclinstance");
		return -1;
        }
	return 1;
}

#/////////////////////////////////////////////////////////////////////////////

1;
__END__

=head1 SEE ALSO

L<http://cwiki.apache.org/VCL/>

=cut

